/**
 * 
 */
package fluo.desktopserver;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import com.dusbabek.lib.id3.Mp3File;
import com.dusbabek.lib.id3.Tag;

import fluo.androidclient.FluoMessage;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;

import fluo.centralserver.CentralServerDetails;
import fluo.centralserver.ClientInformation;
import fluo.centralserver.RegisterDesktop;
import fluo.http.FluoHttpServer;

/**
 * @author maherbeg
 *
 * Created: Apr 11, 2009
 * Modified: Apr 11, 2009
 */
public class DesktopServer extends WindowAdapter implements CentralServerDetails, Runnable, ActionListener {

	RegisterDesktop ServerDetails;
	HashMap<String, ArrayList<String>> MusicList; //Key = Parent Directory, Value = List of mp3 files under the directory
	
	FluoHttpServer hs;
	
	//GUI Details
	JFrame MainWindow;
	JButton AddFolder;
	JButton RemoveFolder;
	JButton AddUser;
	JList FolderList, UserList;
	JLabel StatusBar;
	DefaultListModel FolderListModel, UserListModel;
	JFileChooser chooser;
	
	private int port = 1234; //default port number
	private String host =  "128.62.233.130";
	
	
	private boolean debug = true;
	
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {	
		//Let's listen for clients
		ServerSocket MainSocket = null;
		try {
			MainSocket = new ServerSocket(port);
			MainSocket.setReuseAddress(true);
			//System.out.println("DesktopServer.run(): Main Socket ip: " + InetAddress.getLocalHost());
			host = InetAddress.getLocalHost().toString().split("/")[1];
			System.out.println("DesktopServer.run(): Main Socket ip: " + host);
			//MainSocket.bind(new InetSocketAddress(ip, port));

		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		while(MainSocket != null){
			try {
				System.out.println("Desktop Server: Waiting for new connection..." + MainSocket.getLocalPort());
				Socket register = MainSocket.accept();
				System.out.println("Client connected...");
				ObjectOutputStream oos = new ObjectOutputStream(register.getOutputStream());
				ObjectInputStream ois = new ObjectInputStream(register.getInputStream());
				
				Object receiveData = null;
				try {
					receiveData = ois.readObject();
				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//while(ois.available() == 0);
				//while(ois.available() != 0) receiveData+= ois.readChar();
				System.out.println(receiveData);
				if(receiveData != null && receiveData instanceof FluoMessage &&
						((FluoMessage)receiveData).getSingleString().equalsIgnoreCase("android")){
					System.out.println("DesktopServer: Android client found");
					ArrayList<String> MusicInfo = getMusic();
					FluoMessage ret = new FluoMessage(MusicInfo);
					oos.writeObject(ret);
					//Get ID3 data of all the files
			        ArrayList<String> songsData = new ArrayList<String>();
			        for(int count = 0; count<MusicInfo.size();count++){
			        	try{
					        File songFile = new File(MusicInfo.get(count));
					        Mp3File mp3 = new Mp3File(songFile);
					        Tag tag = mp3.getTag();
					        songsData.add(tag.getTitle()+" - "+tag.getArtist());
					        //System.out.println(tag.getTitle()+" - "+tag.getArtist());
			        	} catch(Exception ex){
			        		System.out.println("Error with ID3 tag for file " + MusicInfo.get(count));
			        		ex.printStackTrace();
			        		
			        	}
			        }
					ret = new FluoMessage(songsData);
					oos.writeObject(ret);
					
					
				}
				register.close();
				
			} catch (UnknownHostException e) {
				System.out.println("DesktopServer.DesktopServer(): Can not find the Central Server, check to see if it is running.");
				e.printStackTrace();
			} catch (IOException e) {
				System.out.println("DesktopServer.DesktopServer(): Issue with input/output system");
				e.printStackTrace();
			} /*catch (ClassNotFoundException e) {
				System.out.println("DesktopServer.DesktopServer(): Incorrect object sent over stream.");
				e.printStackTrace();
			}*/
		}

	}
	
	public DesktopServer(){
		getParams();
		
		if(ServerDetails == null){
			ServerDetails = new RegisterDesktop(
										JOptionPane.showInputDialog("Please select a name for your server."),
										host, port);
			
			//Save this data
			setParams();
		}
		
			
			MusicList = new HashMap<String, ArrayList<String>>();
			
			//We have registered our selves to the main server, let us set up our GUI
			MainWindow = new JFrame("Fluo Music Server - " + ServerDetails);
			MainWindow.addWindowListener((WindowListener) this);
			MainWindow.setSize(400,400);
			
			JPanel content = new JPanel(new GridBagLayout());
			GridBagConstraints gbc = new GridBagConstraints();
			gbc.fill = GridBagConstraints.HORIZONTAL;
			gbc.weightx = 0.5;
			
			//TODO Add Icons for the buttons
			
			gbc.gridx = 0; gbc.gridy = 0;
			AddFolder = new JButton("Add Folder");
			AddFolder.setActionCommand("Add Folder"); AddFolder.addActionListener(this);
			AddFolder.setMnemonic(KeyEvent.VK_A);
			content.add(AddFolder, gbc);
			
			gbc.gridx = 1; gbc.gridy = 0;
			RemoveFolder = new JButton("Remove Folder");
			RemoveFolder.setActionCommand("Remove Folder"); RemoveFolder.addActionListener(this);
			RemoveFolder.setMnemonic(KeyEvent.VK_R);
			content.add(RemoveFolder,gbc);
			
			gbc.gridx = 2; gbc.gridy =0;
			AddUser = new JButton("Add User");
			AddUser.setActionCommand("Add User"); AddUser.addActionListener(this);
			AddUser.setMnemonic(KeyEvent.VK_U);
			content.add(AddUser,gbc);
			
			gbc.gridx = 0; gbc.gridy =1; gbc.gridwidth=3; gbc.weighty = 1.0;
			FolderListModel = new DefaultListModel(); FolderList = new JList(FolderListModel);
			
			FolderList.setSize(400, 600);
			FolderList.setVisible(true);
			FolderList.setVisibleRowCount(10);
			JScrollPane FolderScroll = new JScrollPane(FolderList);
			content.add(FolderScroll, gbc);
			
			/*gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 3; gbc.weighty = 1.0;
			UserListModel = new DefaultListModel(); UserListModel.add(0,""); UserList = new JList(UserListModel);
			UserList.setSize(400,200); UserList.setVisible(true); UserList.setVisibleRowCount(5);
			JScrollPane UserScroll = new JScrollPane(UserList);
			content.add(UserScroll);*/
			
			gbc.gridx = 0; gbc.gridy = 4; gbc.gridwidth=3;
			StatusBar = new JLabel("Ready to serve files.");
			StatusBar.setSize(400, 25);
			content.add(StatusBar, gbc);
			
			MainWindow.add(content);
			MainWindow.setVisible(true);
			MainWindow.pack();
			
			setMessage("Connecting to main server and sending our details");
			
			//Connect to the central server and register ourselves
			try {
				Socket register = new Socket(InetAddress.getByName(CentralServerDetails.ip),CentralServerDetails.port);
				
				ObjectOutputStream oos = new ObjectOutputStream(register.getOutputStream());
				ObjectInputStream ois = new ObjectInputStream(register.getInputStream());
				
				oos.writeObject(new FluoMessage("desktopserver"));
				oos.flush();
				oos.writeObject(ServerDetails);
				oos.flush();
				
				Object receiveData = ois.readObject();
				
				if(receiveData != null){
					if(receiveData instanceof RegisterDesktop){
						ServerDetails = (RegisterDesktop)receiveData;
						if(!ServerDetails.isAccepted()){
							//TODO Deal with unaccepted Server Details
						}
					}
				}
				
				register.close();
				
				//register a default user
				register = new Socket(InetAddress.getByName(CentralServerDetails.ip),CentralServerDetails.port);	
				oos = new ObjectOutputStream(register.getOutputStream());
				ois = new ObjectInputStream(register.getInputStream());
				 
				oos.writeObject(new FluoMessage("desktopserver"));
				oos.writeObject(new ClientInformation("ee360p", "pw")); //send off the client name we want to add
				oos.writeObject(ServerDetails); //send off the server details of this server
				oos.flush();
				
				register.close();
			} catch (UnknownHostException e) {
				System.out.println("DesktopServer.DesktopServer(): Can not find the Central Server, check to see if it is running.");
				e.printStackTrace();
			} catch (IOException e) {
				System.out.println("DesktopServer.DesktopServer(): Issue with input/output system");
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				System.out.println("DesktopServer.DesktopServer(): Incorrect object sent over stream.");
				e.printStackTrace();
			}
			
			setMessage("Starting HTTP Server");
			//Start up HTTP Server
			hs = new FluoHttpServer();
			
			getFolderList();
			
			//Start our main thread
			Thread t= new Thread(this);
			t.start();
	}
	
	private void setParams() {
		Connection conn = null;
		Statement stat = null;
		PreparedStatement prep = null;
		try {
			System.out.println("DesktopServer.getFolderList(): Reading client list from db");
			Class.forName("org.sqlite.JDBC");
			conn = DriverManager.getConnection("jdbc:sqlite:fluo.db");
			if(conn == null) return; //return an empty list
			
			//Now let's retrieve all the clients from our database
			stat = conn.createStatement();
			if(stat == null) return; 
			
			stat.executeUpdate("CREATE TABLE IF NOT EXISTS params (key,value);");
			prep = conn.prepareStatement("INSERT INTO params values(?,?)");
			prep.setString(1, "servername");
			prep.setString(2,ServerDetails.getServerName());
			prep.execute();
			
			prep.setString(1,"port");
			prep.setInt(2, ServerDetails.getPort());
			prep.execute();
			
			if(prep != null) prep.close();
			if(stat != null)stat.close();
			if(conn != null) conn.close();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	private void getFolderList() {
		Connection conn = null;
		Statement stat = null;
		try {
			System.out.println("DesktopServer.getFolderList(): Reading client list from db");
			Class.forName("org.sqlite.JDBC");
			conn = DriverManager.getConnection("jdbc:sqlite:fluo.db");
			if(conn == null) return; //return an empty list
			
			//Now let's retrieve all the clients from our database
			stat = conn.createStatement();
			if(stat == null) return; 
			
			stat.executeUpdate("CREATE TABLE IF NOT EXISTS folders (foldername);");
			ResultSet rs = stat.executeQuery("SELECT * FROM folders;"); 
			if(rs == null) return;
			
			while(rs.next()){
				String folder = rs.getString("foldername");
				if(debug) System.out.println("DesktopServer.getFolderList(): " + folder + " found in db");
				new FolderScanner(this,new File(folder), false);
			}
			
			rs.close();
			
			if(stat != null)stat.close();
			if(conn != null) conn.close();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void windowClosing(WindowEvent we){	
		MainWindow.dispose();
	}

	public void actionPerformed(ActionEvent e) {
		if(e.getActionCommand().equals("Add Folder")){
			if(debug) System.out.println("DesktopServer.actionPerformed(Adding folder)");
			chooser = new JFileChooser();
		    chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		    chooser.setMultiSelectionEnabled(false);
			
			int retVal = chooser.showOpenDialog(MainWindow);
			if(retVal == JFileChooser.APPROVE_OPTION){
				if(debug) System.out.println("Desktop.actionPerformed(Adding Folder): Approved the selection of " + chooser.getSelectedFile());
				new FolderScanner(this,chooser.getSelectedFile(), true);
			}
		} else if(e.getActionCommand().equals("Remove Folder")){
			if(debug) System.out.println("DesktopServer.actionPerformed(Removing folder)");
			if(FolderList.getSelectedIndices() != null){
				int remove[] = FolderList.getSelectedIndices();
				Connection conn = null;
				Statement stat = null;
				PreparedStatement prep = null;
				
				//Initialize our database and create a db connection
				if(debug) System.out.println("DesktopServer.actionPerformed(): Attempting to remove folders from database");
				try {
					Class.forName("org.sqlite.JDBC");
					conn = DriverManager.getConnection("jdbc:sqlite:fluo.db");
					if(conn == null) return; //return an empty list
					
					//Now let's retrieve all the clients from our database
					stat = conn.createStatement();
					if(stat == null) return; 
					
					stat.executeUpdate("CREATE TABLE IF NOT EXISTS folders (foldername);");
					stat.close();
					//Prepare our deletion routine
					prep = conn.prepareStatement("DELETE FROM folders WHERE foldername=?;");
				} catch (ClassNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (SQLException e2) {
					// TODO Auto-generated catch block
					e2.printStackTrace();
				}
				
				for(int i=remove.length-1; i >= 0; i--){
					if(debug) System.out.println("DesktopServer.actionPerformed(Removing Folder): Index to remove " + i);
					String toRemove = (String)FolderListModel.get(remove[i]).toString();
					setMessage("Removing " + toRemove);
					
					try {
						prep.setString(1, toRemove);
						prep.execute();
						
						if(debug) System.out.println("DesktopServer.actionPerformed(): Removed folder " + toRemove + " from database");
						if(prep != null) prep.close();
					} catch (SQLException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
	
					MusicList.remove(toRemove);
					FolderListModel.removeElementAt(remove[i]);
					setMessage("Removed " + toRemove);
					//TODO Scan file list and remove anything under this folder
				}
				
				if(conn != null)
					try {
						conn.close();
					} catch (SQLException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
			}
		} else if(e.getActionCommand().equals("Add User")){
			if(debug) System.out.println("DesktopServer.actionPerformed(Adding User");
			Socket register;
			try {
				register = new Socket(InetAddress.getByName(CentralServerDetails.ip),CentralServerDetails.port);
				ObjectOutputStream oos = new ObjectOutputStream(register.getOutputStream());
				//ObjectInputStream ois = new ObjectInputStream(register.getInputStream());
				
				String toadd = JOptionPane.showInputDialog("Permit the following user to use this server");
				oos.writeObject(new FluoMessage("desktopserver"));
				oos.writeObject(new ClientInformation(toadd, "pw")); //send off the client name we want to add
				oos.writeObject(ServerDetails); //send off the server details of this server
				register.close();
			} catch (UnknownHostException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
	}
	
	public void setMessage(String status){
		StatusBar.setText(status);
	}
	
	public HashMap<String, ArrayList<String>> getMusicList(){
		return MusicList;
	}
	
	public ArrayList<String> getMusic(){
		ArrayList<String> retVal = new ArrayList<String>();
		for(ArrayList<String> cur : MusicList.values()){
			retVal.addAll(cur);
		}
		
		return retVal;
	}
	
	public boolean getDebug(){
		return debug;
	}
	
	public void addFolder(String folderName, ArrayList<String> folderList, boolean insert){
		Connection conn = null;
		Statement stat = null;
		PreparedStatement prep = null;
		if(insert == true){
			try {
				if(debug) System.out.println("DesktopServer.addFolder(): Attempting to add " + folderName + " to database");
				Class.forName("org.sqlite.JDBC");
				conn = DriverManager.getConnection("jdbc:sqlite:fluo.db");
				if(conn == null) return; //return an empty list
				
				//Now let's retrieve all the clients from our database
				stat = conn.createStatement();
				if(stat == null) return; 
				
				stat.executeUpdate("CREATE TABLE IF NOT EXISTS folders (foldername);");
				
				prep = conn.prepareStatement("INSERT INTO folders values (?);");
				prep.setString(1, folderName);
				prep.execute();
				
				if(debug) System.out.println("DesktopServer.addFolder(): Inserted folder into database");
				
				if(prep != null) prep.close();
				if(stat != null)stat.close();
				if(conn != null) conn.close();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		MusicList.put(folderName, folderList);
		FolderListModel.addElement(folderName);
		MainWindow.pack();
	}
	
	private void getParams(){
		Connection conn = null;
		Statement stat = null;
		PreparedStatement prep = null;
		
		try {
			if(debug) System.out.println("DesktopServer.getParams(): Attempting to read parameters from database");
			Class.forName("org.sqlite.JDBC");
			conn = DriverManager.getConnection("jdbc:sqlite:fluo.db");
			if(conn == null) return; //return an empty list
			
			//Now let's retrieve all the clients from our database
			stat = conn.createStatement();
			if(stat == null) return; 
			
			stat.executeUpdate("CREATE TABLE IF NOT EXISTS params (key,value);");
			stat.close();
			
			prep = conn.prepareStatement("SELECT value FROM params WHERE key=?;");
			prep.setString(1, "servername");
			ResultSet rs = prep.executeQuery();
			while(rs.next())
				ServerDetails = new RegisterDesktop(rs.getString("value"), host, port);
			rs.close();
			
			prep.setString(1,"port");
			rs = prep.executeQuery();
			while(rs.next()){
				ServerDetails.setPort(rs.getInt("value"));
				port = ServerDetails.getPort();
			}
			rs.close();
			
			
			if(debug) System.out.println("DesktopServer.addFolder(): Inserted folder into database");
			
			if(prep != null) prep.close();
			if(stat != null)stat.close();
			if(conn != null) conn.close();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public static void main(String args[]){
		@SuppressWarnings("unused")
		DesktopServer ds = new DesktopServer();
	}
}
